﻿
module Sustainalytics.SearchBox {

    // get/create search box module
    var searchBoxModule = angular.module('Sustainalytics.SearchBox');


    class SearchBoxBehavior {

        private controller: SearchBoxController;
        private searchInputEl;
        private searchResultsEl;
        private searchOnEmptyData;
        private firstFocus: boolean;
        private textProp: string;

        constructor(
            private scope,
            private element,
            private attrs,
            private $injector: ng.auto.IInjectorService,
            private $timeout: ng.ITimeoutService

            ) {

            var ctx = this;

            ctx.controller = ctx.scope.search;
            ctx.firstFocus = true;
            ctx.textProp = null;

            ctx.findInDom();
            ctx.setOptions();
            ctx.searchOnInputChangeThrottled();
            ctx.searchOnScroll();
            ctx.handleSearchBoxVisibility();
            ctx.handleScrollReset();
            ctx.blockOuterScrolls();
            ctx.handleTextChange();
         
        }

        // search for DOM elements and attributes
        private findInDom() {
            var ctx = this;

            ctx.searchInputEl = ctx.element.find('.sustainalytics-searchbox-input:first');
            ctx.searchResultsEl = ctx.element.find('.sustainalytics-searchbox-results:first');
        }

        // set search box options
        private setOptions() {
            var ctx = this;

            // determine whether to search on empty data
            var searchEmpty = ctx.element.attr('data-searchbox-searchempty');
            ctx.controller.searchOnEmptyData = searchEmpty !== null && typeof (searchEmpty) !== 'undefined';

            // set text property
            ctx.textProp = ctx.element.attr('data-searchbox-text');

            // set search box service addin
            ctx.setSearchService();
        }

        // Set custom search service on controller.
        private setSearchService() {
            var ctx = this;

            // get custom service based on attribute
            var searchServiceName = ctx.element.attr('data-searchbox-service');

            if (!searchServiceName)
                $.error('Sustainalytics.SearchBox :: sustainalyticsSearchbox directive :: data-searchbox-service does not contain search service name.');

            // get search service
            var searchService = ctx.$injector.get(searchServiceName);

            // associate custom search service
            var searchController: SearchBoxController = ctx.scope.search;
            searchController.searchServiceName = searchServiceName;
            searchController.searchService = searchService;

            
        }

        // register input onchange event handler to search for new companies - search frequency is throttled
        private searchOnInputChangeThrottled() {
            var ctx = this;

            // controller
            var searchController: SearchBoxController = ctx.scope.search;

            // search frequency
            var inputChangeSearchFrequency: number = searchController.inputChangeSearchFrequency;

            // handle input change
            ctx.searchInputEl.on('propertychange input', function () {
                ctx.$timeout(function () {
                    ctx.scope.$apply(function () {
                        searchController.search(true);
                    });
                });
            });
        }

        // register scroll event handler to query the next page
        private searchOnScroll() {
            var ctx = this;

            // controller
            var searchController: SearchBoxController = ctx.scope.search;

            // handle scroll
            ctx.searchResultsEl.on('scroll', function (ev) {
                // get scrollbar element
                var target = $(ev.target);

                // check if it reached the max scroll size and go to next page
                if (target[0].scrollHeight == (target.scrollTop() + target.outerHeight())) {
                    ctx.$timeout(function () {
                        ctx.scope.$apply(function () {
                            searchController.searchNextPage(false);
                        });
                    });
                }
            });
        }

        // logic for setting the search box visibility
        private handleSearchBoxVisibility() {
            var ctx = this;

            // do not propagate click events from search box to parent
            ctx.element.click(function () {
                return false;
                //if (event.stopPropagation) {
                //    event.stopPropagation();
                //} else {
                //event.cancelBubble = true;
                //event.returnValue = false;
                //}
            });

            // set as invisible if body is clicked
            $('body').click(function () {
                ctx.$timeout(function () {
                    ctx.scope.$apply(function () {
                        ctx.setSearchBoxVisibility(false);
                    });
                });
            });

            ctx.element.blur(function() {
                ctx.$timeout(function () {
                    ctx.scope.$apply(function () {
                        ctx.setSearchBoxVisibility(false);
                    });
                });
            });

            // set as visible if search input gets focused
            ctx.searchInputEl.focus(function () {
                ctx.$timeout(function () {
                    ctx.scope.$apply(function () {

                        if (ctx.controller.searchOnEmptyData && ctx.firstFocus) {
                            ctx.controller.search(false).then(function () {
                                ctx.setSearchBoxVisibility(true);
                                ctx.firstFocus = false;
                            });
                        } else {
                            ctx.setSearchBoxVisibility(true);
                        }
                    });
                });
            });

        }

        // set visibility
        private setSearchBoxVisibility(isVisible: boolean) {
            var ctx = this;
            ctx.scope.search.isVisible = isVisible;
            if (!isVisible) {
                ctx.fireClosed();
            }
        }

        // hadle scroll reset when changing
        private handleScrollReset() {
            var ctx = this;

            ctx.scope.$watch('search.lastSkip', function () {
                var skip = ctx.scope.search.lastSkip;

                // reset scroll when cursor is at the first position
                if (skip === 0) {
                    ctx.searchResultsEl.scrollTop(0);
                }
            });
        }

        // block outer scrollbars
        private blockOuterScrolls() {
            var ctx = this;

            ctx.searchResultsEl.bind('mousewheel', function (e) {

                var d = e.originalEvent.deltaY;

                var height = ctx.searchResultsEl.outerHeight();
                var scrollHeight = ctx.searchResultsEl.get(0).scrollHeight;

                if ((this.scrollTop === (scrollHeight - height) && d > 0) || (this.scrollTop === 0 && d < 0)) {
                    e.preventDefault();

                    if (e.stopPropagation) {
                        e.stopPropagation();
                    } else {
                        e.cancelBubble = true;
                        e.returnValue = false;
                    }
                }
            });
        }

        // fire search box closed
        private fireClosed() {
            var ctx = this;
            ctx.scope.$emit('searchBoxClosed');
        }

        // handle ng-model changes
        private handleTextChange() {
            var ctx = this;

            // bind between outer text property and current text model
            if (ctx.textProp) {
                
                (<ng.IScope>ctx.scope).$eval(ctx.textProp + ' = search.searchData');

                // watch controller search data
                ctx.scope.$watch('search.searchData', function (newValue) {

                    if (ctx.textProp == 'universeSearchData') {
                        if (newValue == "") {                           
                            ctx.scope.$parent.filterEdit.accountUniverseSelected(0);
                        }
                    }

                    if (ctx.textProp == 'portfoliosAndUniversesSearchData') {
                        if (newValue == "") {                           
                           
                            ctx.scope.$parent.filterEdit.accountUniverseSelected(0);
                            ctx.scope.$parent.filterEdit.accountPortfolioSelected("");                               
                        }
                    }                   

                    if (ctx.textProp == 'peerGroupSearchData') {
                        if (newValue == "") {                          
                            ctx.scope.$parent.filterEdit.rootPeerGroupSelected("");
                        }
                    }

                    if (ctx.textProp == 'regionSearchData') {
                        if (newValue == "") {                            
                            ctx.scope.$parent.filterEdit.regionSelected("");
                        }
                    }

                    if (ctx.textProp == 'countrySearchData') {
                        if (newValue == "") {                           
                            ctx.scope.$parent.filterEdit.countrySelected("");
                        }
                    }

                    if (ctx.textProp == 'companyTypeSearchData') {
                        if (newValue == "") {                           
                            ctx.scope.$parent.filterEdit.companyTypeSelected(0);
                        }
                    }

                    (<ng.IScope>ctx.scope).$eval(ctx.textProp + ' = search.searchData');
                });

                // watch outer property
                ctx.scope.$parent.$watch(ctx.textProp, function (newValue) {
                    var oldValue = ctx.controller.searchData;

                    ctx.controller.searchData = newValue;

                    // search
                    if (oldValue !== newValue) {
                        ctx.controller.search(false);
                    }

                    
                });

                ctx.scope.$parent.$watch("filterEdit.currentCountry.name", function (newValue) {

                    (<ng.IScope>ctx.scope).$eval(ctx.textProp + ' = search.searchData');

                    if (ctx.textProp == 'countrySearchData') {
                        if (ctx.scope.$parent.filterEdit.currentCountry != undefined)
                            ctx.controller.searchData = ctx.scope.$parent.filterEdit.currentCountry.name;
                    }
                });

                ctx.scope.$parent.$watch("filterEdit.currentPeerGroup.name", function (newValue) {

                    (<ng.IScope>ctx.scope).$eval(ctx.textProp + ' = search.searchData');

                    if (ctx.textProp == 'peerGroupSearchData') {
                        if (ctx.scope.$parent.filterEdit.currentPeerGroup != undefined)
                            ctx.controller.searchData = ctx.scope.$parent.filterEdit.currentPeerGroup.name;
                    }
                });

                ctx.scope.$parent.$watch("filterEdit.currentRegion.name", function (newValue) {

                    (<ng.IScope>ctx.scope).$eval(ctx.textProp + ' = search.searchData');

                    if (ctx.textProp == 'regionSearchData') {
                        if (ctx.scope.$parent.filterEdit.currentRegion != undefined)
                            ctx.controller.searchData = ctx.scope.$parent.filterEdit.currentRegion.name;
                    }
                });

                ctx.scope.$parent.$watch("filterEdit.currentCompanyType.name", function (newValue) {

                    (<ng.IScope>ctx.scope).$eval(ctx.textProp + ' = search.searchData');

                    if (ctx.textProp == 'companyTypeSearchData') {
                        if (ctx.scope.$parent.filterEdit.currentCompanyType != undefined)
                            ctx.controller.searchData = ctx.scope.$parent.filterEdit.currentCompanyType.name;
                    }
                });

                ctx.scope.$parent.$watch("filterEdit.currentPortfolioOrUniverse.name", function (newValue) {

                    (<ng.IScope>ctx.scope).$eval(ctx.textProp + ' = search.searchData');

                    if (ctx.textProp == 'portfoliosAndUniversesSearchData') {
                        if (ctx.scope.$parent.filterEdit.currentPortfolioOrUniverse != undefined)
                            ctx.controller.searchData = ctx.scope.$parent.filterEdit.currentPortfolioOrUniverse.name;
                    }
                });
            }
        }
       
    }


    var directive = function ($injector, $timeout) {
        return {
            // restrict usage for classes
            restrict: 'C',

            // generate child scope
            scope: true,

            // associate search box controller
            controller: 'SearchBoxController',

            // link function for attaching event handlers and applying scope
            link: function (scope, element, attrs) {
                var behavior = new SearchBoxBehavior(scope, element, attrs, $injector, $timeout);
            }
        }
    };


    // create search box directive
    searchBoxModule.directive('sustainalyticsSearchbox', ['$injector', '$timeout', directive]);
}